package com.support.mvc.entity.generator;

import com.utils.util.Util;
import org.apache.commons.lang3.RandomStringUtils;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.Type;

import java.io.Serializable;
import java.time.Instant;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 自定义 id 生成规则（默认）： Base62(时间戳秒数 + 6位自增序列) + 3位随机编码
 *
 * @author 谢长春 2020-07-24
 * @deprecated 建议使用 {@link Base18Generator} 替换。 Base62TimestampIdGenerator 多个服务情况下，可能会有重复的id， 如果需要降低重复概率，可以调整随机编码长度。
 */
public final class Base62TimestampIdGenerator implements IdentifierGenerator {
    /**
     * 随机代码长度
     */
    private static final int RANDOM_CODE_LENGTH = 3;
    /**
     * 自增最小值
     */
    private static final int MIN_VALUE = 100000;
    /**
     * 自增最大值
     */
    private static final int MAX_VALUE = 999900;
    /**
     * 自增系列
     */
    private static final AtomicInteger atomicInteger = new AtomicInteger(MIN_VALUE);

    public Base62TimestampIdGenerator() {
        this.randomCodeLength = RANDOM_CODE_LENGTH;
    }

    /**
     * 构造id生成规则
     *
     * @param randomCodeLength int 随机代码长度，随机代码生成参考 {@link RandomStringUtils#randomAlphanumeric}
     */
    public Base62TimestampIdGenerator(int randomCodeLength) {
        this.randomCodeLength = randomCodeLength;
    }

    /**
     * 随机编码长度， 默认 3 位。生成规则参考：{@link RandomStringUtils#randomAlphanumeric}
     */
    private final int randomCodeLength;

    private static final String BASE_62_CHAR = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    private static final int BASE_62_CHAR_LENGTH = BASE_62_CHAR.length();


    /**
     * 从十进制 转 62 进制
     *
     * @param value {@link Long} 十进制数字
     * @return {@link String} 62进制字符串
     */
    private static String encodeBase62(long value) {
        if (value < BASE_62_CHAR_LENGTH) {
            return BASE_62_CHAR.charAt((int) value) + "";
        }
        final StringBuilder sb = new StringBuilder();
        int rem;
        while (value > 0) {
            rem = (int) (value % BASE_62_CHAR_LENGTH);
            sb.append(BASE_62_CHAR.charAt(rem));
            value = value / BASE_62_CHAR_LENGTH;
        }
        return sb.reverse().toString();
    }

    @Override
    public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException {

    }

    @Override
    public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
        int increment = atomicInteger.getAndIncrement();
        if (increment > MAX_VALUE) {
            synchronized (this) {
                atomicInteger.set(MIN_VALUE);
            }
            increment = atomicInteger.getAndIncrement();
        }
        return encodeBase62(Long.parseLong(String.format("%d%d", Instant.now().getEpochSecond(), increment)))
                .concat(RandomStringUtils.randomAlphanumeric(3));
    }

    public static void main(String[] args) {
        Set<String> ids = new HashSet<>();
        Base62TimestampIdGenerator generator = new Base62TimestampIdGenerator();
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    String id = (String) generator.generate(null, null);
                    System.out.println(id + ":" + Util.decodeBase62(id.substring(0, id.length() - 3)));
                    if (ids.contains(id)) {
                        System.err.println(id + ":" + Util.decodeBase62(id.substring(0, id.length() - 3)));
                    } else {
                        ids.add(id);
                    }
                }
            }).start();
        }
    }
}
